package com.mmall.schedule;

import com.mmall.common.Const;
import com.mmall.common.RedissonManager;
import com.mmall.service.IOrderService;
import com.mmall.utils.PropertiesUtil;
import com.mmall.utils.RedisPoolUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.PreDestroy;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class CloseOrderTask {

    @Autowired
    private IOrderService iOrderService;

    @Autowired
    private RedissonManager redissonManager;

    /**
     * 关闭tomca时调用方法，但是直接killtomcat进程时，不会调用
     */
    @PreDestroy
    public void delLock(){
        RedisPoolUtil.del(Const.RedisLock.CLOSE_ORDER_TASK_LOCK);

    }

    /**
     * 简单单服务调度
     */
//    @Scheduled(cron = "0 */1 * * * ?")//每一分钟*(每个1分钟的整数倍)
    public void closeOrderTaskV1() {
        log.info("关闭订单定时任务启动");
        int hour = Integer.parseInt(PropertiesUtil.getProperty("close.order.task.time.hour", "2"));

//        iOrderService.closeOrder(hour);
        log.info("关闭订单定时任务结束");

    }

    /**
     * 可能发生死锁，例如直接kill掉tomcat进程
     */
//    @Scheduled(cron = "0 */1 * * * ?")
    public void closeOrderTaskV2() {
        log.info("关闭订单定时任务启动");
        long lockTimeout = Long.parseLong(PropertiesUtil.getProperty("lock.timeout", "5000"));

        //设置redis锁时间,使用setNX方法，
        Long setnxResult = RedisPoolUtil.setnx(Const.RedisLock.CLOSE_ORDER_TASK_LOCK,
                String.valueOf(System.currentTimeMillis() + lockTimeout));
        if (setnxResult != null && setnxResult.intValue() == 1) {
            //如果返回已，代表设置成功，获取锁
            thisCloseOrder(Const.RedisLock.CLOSE_ORDER_TASK_LOCK);
        }else{
            log.info("没有获得分布式锁：{}",Const.RedisLock.CLOSE_ORDER_TASK_LOCK);
        }
        log.info("关闭订单定时任务结束");

    }

    @Scheduled(cron = "0 */10 * * * ?")
    public void closeOrderTaskV3() {
        log.info("关闭订单定时任务启动");
        long lockTimeout = Long.parseLong(PropertiesUtil.getProperty("lock.timeout", "5000"));
        Long setnxResult = RedisPoolUtil.setnx(Const.RedisLock.CLOSE_ORDER_TASK_LOCK,
                String.valueOf(System.currentTimeMillis() + lockTimeout));
        if (setnxResult != null && setnxResult.intValue() == 1) {
            //如果返回已，代表设置成功，获取锁
            thisCloseOrder(Const.RedisLock.CLOSE_ORDER_TASK_LOCK);
        }else{
            //为获取到，继续判断，判断时间戳，看是否可以重置并获取到锁
            String lockValueStr = RedisPoolUtil.get(Const.RedisLock.CLOSE_ORDER_TASK_LOCK);
            //当前时间大于设置的锁的值的时间
            if (lockValueStr != null && System.currentTimeMillis() > Long.valueOf(lockValueStr)) {
                //重置锁的值时间，返回旧的值
                String getSetResult = RedisPoolUtil.getSet(Const.RedisLock.CLOSE_ORDER_TASK_LOCK,
                        String.valueOf(System.currentTimeMillis() + lockTimeout));
                //再次用当前时间戳getset。
                //返回给定的key的旧值，->旧值判断，是否可以获取锁
                //当key没有旧值时，即key不存在时，返回nil ->获取锁
                //这里我们set了一个新的value值，获取旧的值。
                //如果两个值相同，表示key没有被其他进程使用过，则获取到锁
                if (getSetResult == null || (getSetResult != null && StringUtils.equals(getSetResult, lockValueStr))) {
                    //执行业务
                    thisCloseOrder(Const.RedisLock.CLOSE_ORDER_TASK_LOCK);
                }else{
                    log.info("没有获得分布式锁：{}",Const.RedisLock.CLOSE_ORDER_TASK_LOCK);
                }
            }else{
                log.info("没有获得分布式锁：{}",Const.RedisLock.CLOSE_ORDER_TASK_LOCK);
            }
        }
        log.info("关闭订单定时任务结束");

    }

    /**
     * 使用redisson实现分布式锁
     */
//    @Scheduled(cron = "0 */1 * * * ?")
    public void closeOrderTaskV4() {
        log.info("关闭订单定时任务启动");

        RLock lock = redissonManager.getRedisson().getLock(Const.RedisLock.CLOSE_ORDER_TASK_LOCK);

        boolean getLock = false;
        try {
            //等待期2秒，租用期5秒,
            if (getLock = lock.tryLock(2, 10, TimeUnit.SECONDS)) {
                log.info("Redisson获取到分布式锁:{},ThreadName:{}", Const.RedisLock.CLOSE_ORDER_TASK_LOCK, Thread.currentThread().getName());
                int hour = Integer.parseInt(PropertiesUtil.getProperty("close.order.task.time.hour", "2"));
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
//        iOrderService.closeOrder(hour);
            } else {
                log.info("Redisson没有获取到分布式锁:{},ThreadName:{}", Const.RedisLock.CLOSE_ORDER_TASK_LOCK, Thread.currentThread().getName());
            }
        } catch (InterruptedException e) {
            log.error("Redisson分布式锁获取异常",e);
        }finally {
            if(!getLock){
                return;
            }
            lock.unlock();
            log.info("Redisson分布式锁释放锁");
        }
    }

    private void thisCloseOrder(String lockName) {
        RedisPoolUtil.expire(lockName, 5);//设置有效期50秒，防止死锁
        log.info("获取锁:{},ThreadName:{}", Const.RedisLock.CLOSE_ORDER_TASK_LOCK, Thread.currentThread().getName());
        int hour = Integer.parseInt(PropertiesUtil.getProperty("close.order.task.time.hour", "2"));
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
//        iOrderService.closeOrder(hour);
        RedisPoolUtil.del(Const.RedisLock.CLOSE_ORDER_TASK_LOCK);
        log.info("释放锁:{},ThreadName:{}",Const.RedisLock.CLOSE_ORDER_TASK_LOCK,Thread.currentThread().getName());
        log.info("===============================");
    }
}
